package heap;

import java.io.IOException;

import exceptions.InvalidTupleSizeException;
import exceptions.InvalidTypeException;

import storage.data.PageID;
import Common.ByteArray;
import Common.Convert;
import Common.GlobalConst;
import Common.Types;
import Transaction.Transaction;

public class Tuple {
	/**
	 * Maximum size of any tuple
	 */
	public static final int maxSize = GlobalConst.PAGESIZE;

	/**
	 * a byte array to hold data
	 */
	private byte[] data;

	/**
	 * start position of this tuple in data[]
	 */
	private int tupleOff;

	/**
	 * length of this tuple
	 */
	private int tupleLen;

	/**
	 * Number of fields in this tuple
	 */
	private short fldCnt;

	/**
	 * array of offsets of the fields
	 */
	private short[] fldOffset;


	public Tuple(byte[] tuple, int offset, int length) {
		data = tuple;
		tupleOff = offset;
		tupleLen = length;
	}

	public Tuple(Tuple fromTuple,Transaction tx) {
		data = fromTuple.getTupleByteArray(tx);
		tupleLen = fromTuple.getLength();
		tupleOff = 0;
		fldCnt = fromTuple.noOfFlds();
		fldOffset = fromTuple.copyFldOffset();
	}

	public Tuple(int size) {
		data = new byte[size];
		tupleOff = 0;
		tupleLen = size;
	}

	public void tupleCopy(Tuple fromTuple,Transaction tx,String table,PageID pageno) throws Exception{
		byte[] temparray = fromTuple.getTupleByteArray(tx);
		tx.setBytes(table,pageno,temparray, 0, data, tupleOff, tupleLen);
		// fldCnt = fromTuple.noOfFlds();
		// fldOffset = fromTuple.copyFldOffset();
	}

	public int getLength() {
		return tupleLen;
	}

	public short size() {
		return ((short) (fldOffset[fldCnt] - tupleOff));
	}

	public int getOffset() {
		return tupleOff;
	}

	public byte[] getTupleByteArray(Transaction tx) {
		byte[] tuplecopy = new byte[tupleLen];
		System.arraycopy(data, tupleOff, tuplecopy, 0, tupleLen);
		//System.out.println(String.valueOf(tuplecopy));
		return tuplecopy;
	}

	public byte[] returnTupleByteArray() {
		return data;
	}

	public short noOfFlds() {
		return fldCnt;
	}

	public short[] copyFldOffset() {
		short[] temp = new short[fldCnt + 1];
		for (int i = 0; i <= fldCnt; i++) {
			temp[i] = fldOffset[i];
		}
		return temp;
	}

	public int getIntFld(int fldNo,Transaction tx,String table) throws Exception {
		return Convert.getIntValue(fldOffset[fldNo - 1], data,tx,table);
	}

	public float getFloFld(int fldNo,Transaction tx,String table) throws Exception {
		return Convert.getFloValue(fldOffset[fldNo - 1], data,tx,table);
	}

	public String getStrFld(int fldNo,Transaction tx,String table) throws IOException {
		try {
			//System.out.println("offset:"+fldOffset[fldNo-1]);
			String str = Convert.getStrValue(fldOffset[fldNo - 1], data,tx,table);
			return str;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	public char getcharFld(int fldNo,Transaction tx,String table) throws Exception {
		return Convert.getCharValue(fldOffset[fldNo - 1], data,tx,table);
	}

	public Tuple setIntFld(int fldNo, int val,Transaction tx,String table,PageID pageNo) throws Exception {
		Convert.setIntValue(val, fldOffset[fldNo - 1], data,tx,table,pageNo);
		return this;
	}

	public Tuple setFloFld(int fldNo, float val,Transaction tx,String table,PageID pageNo) throws Exception {
		Convert.setFloValue(val, fldOffset[fldNo - 1], data,tx,table,pageNo);
		return this;
	}

	public Tuple setStrFld(int fldNo, String val,Transaction tx,String table,PageID pageNo) throws Exception {
		Convert.setStrValue(val, fldOffset[fldNo - 1], data,tx,table,pageNo);
		return this;
	}

	
	
	public Tuple SsetIntFld(int fldNo, int val) throws Exception {
		ByteArray.writeInt(data , fldOffset[fldNo - 1],val);
		return this;
	}

	public Tuple SsetFloFld(int fldNo, float val) throws Exception {
		ByteArray.writeFloat(data , fldOffset[fldNo - 1],val);
		
		return this;
	}

	public Tuple SsetStrFld(int fldNo, String val) throws Exception {
//		System.out.println("floNo:"+fldNo+" "+val.toString());
		ByteArray.writeString(data , fldOffset[fldNo - 1],val);
		
		return this;
	}
	/**
	 * setHdr will set the header of this tuple.
	 * 
	 * @param numFlds
	 *            number of fields
	 * @param types[]
	 *            contains the types that will be in this tuple
	 * @param strSizes[]
	 *            contains the sizes of the string
	 * 
	 * @exception IOException
	 *                I/O errors
	 * @exception InvalidTypeException
	 *                Invalid tupe type
	 * @exception InvalidTupleSizeException
	 *                Tuple size too big
	 * 
	 */

	public void setHdr(short numFlds, Types types[], short strSizes[])
			throws Exception, InvalidTypeException, InvalidTupleSizeException {
		/*
		 * if ((numFlds + 2) * 2 > maxSize) throw new
		 * InvalidTupleSizeException(null, "TUPLE: TUPLE_TOOBIG_ERROR");
		 */

		fldCnt = numFlds;
		ByteArray.writeShort(data, tupleOff, numFlds);
		fldOffset = new short[numFlds + 1];
		int pos = tupleOff + 2; // start position for fldOffset[]

		// sizeof short =2 +2: array siaze = numFlds +1 (0 - numFilds) and
		// another 1 for fldCnt
		fldOffset[0] = (short) ((numFlds + 2) * 2 + tupleOff);

		ByteArray.writeShort(data, pos,fldOffset[0]);
		pos += 2;
		short strCount = 0;
		short incr;
		int i;

		for (i = 1; i < numFlds; i++) {
			switch (types[i - 1].attrType) {
			case Types.BOOLEAN:; 
			case Types.INT:
				incr = 4;
				break;

			case Types.FLOAT:
				incr = 4;
				break;

			case Types.VARCHAR:;
			case Types.DATETIME:;
			case Types.TIMESTAMP:;
			case Types.DECIMAL:;
			case Types.CHAR:
				incr = (short) (strSizes[strCount] + 2); // strlen in bytes =
				// strlen +2
				//System.out.println("haha:"+strSizes[strCount] + 2);
				strCount++;
				break;

			default:
				throw new InvalidTypeException(null, "TUPLE: TUPLE_TYPE_ERROR");
			}
			fldOffset[i] = (short) (fldOffset[i - 1] + incr);
			ByteArray.writeShort(data,pos,fldOffset[i]);
			pos += 2;

		}
		switch (types[numFlds - 1].attrType) {
		case Types.BOOLEAN:;
		case Types.INT:
			incr = 4;
			break;

		case Types.FLOAT:
			incr = 4;
			break;
		case Types.VARCHAR:;
		case Types.DATETIME:;
		case Types.TIMESTAMP:; 
		case Types.DECIMAL:;
		case Types.CHAR:
			incr = (short) (strSizes[strCount] + 2); // strlen in bytes =
			// strlen +2
			break;

		default:
			throw new InvalidTypeException(null, "TUPLE: TUPLE_TYPE_ERROR");
		}

		fldOffset[numFlds] = (short) (fldOffset[i - 1] + incr);
		ByteArray.writeShort(data, pos, fldOffset[numFlds]);

		tupleLen = fldOffset[numFlds] - tupleOff;

		if (tupleLen > maxSize) {
			throw new InvalidTupleSizeException(null,
					"TUPLE: TUPLE_TOOBIG_ERROR");
		}
	}

}
